package AES;

import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * Java AES-GCM 加密解密简单案例
 */
public class AesGcmDemo {
    // 算法常量
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/GCM/NoPadding"; // GCM模式无需填充
    private static final int KEY_SIZE = 128; // 密钥长度（128/192/256，256需JCE无限制权限文件）
    private static final int IV_LENGTH = 12; // 初始化向量长度（12字节=96位，GCM推荐）
    private static final int TAG_LENGTH = 16; // 认证标签长度（16字节=128位，防篡改）

    public static void main(String[] args) {
        try {
            // 1. 生成AES密钥（实际应用中需安全存储密钥，而非临时生成）
            String secretKey = generateAesKey();
            System.out.println("生成的AES密钥（Base64编码）：" + secretKey);

            // 2. 待加密的明文
            String plainText = "Hello AES-GCM! 这是一个Java加密案例";
            System.out.println("原始明文：" + plainText);

            // 3. 加密
            String cipherText = encrypt(plainText, secretKey);
            System.out.println("加密后的密文（Base64编码）：" + cipherText);

            // 4. 解密
            String decryptedText = decrypt(cipherText, secretKey);
            System.out.println("解密后的明文：" + decryptedText);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成AES密钥（128位），返回Base64编码字符串
     */
    public static String generateAesKey() throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
        keyGenerator.init(KEY_SIZE, new SecureRandom()); // 安全随机数生成密钥
        SecretKey secretKey = keyGenerator.generateKey();
        return Base64.getEncoder().encodeToString(secretKey.getEncoded());
    }

    /**
     * 加密：明文 -> 密文（Base64编码，格式：IV+密文+认证标签）
     * @param plainText 待加密明文
     * @param secretKey Base64编码的AES密钥
     * @return 加密后的Base64字符串
     */
    public static String encrypt(String plainText, String secretKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        // 1. 解码密钥
        byte[] keyBytes = Base64.getDecoder().decode(secretKey);
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, ALGORITHM);

        // 2. 生成随机IV（初始化向量，必须唯一，无需保密）
        byte[] iv = new byte[IV_LENGTH];
        new SecureRandom().nextBytes(iv);

        // 3. 初始化加密器（GCM模式）
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        GCMParameterSpec gcmSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv); // 标签长度以bit为单位
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);

        // 4. 加密（返回：密文+认证标签）
        byte[] cipherTextWithTag = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

        // 5. 拼接IV+密文+标签（IV需随密文一起传输，解密时需要）
        byte[] result = new byte[IV_LENGTH + cipherTextWithTag.length];
        System.arraycopy(iv, 0, result, 0, IV_LENGTH);
        System.arraycopy(cipherTextWithTag, 0, result, IV_LENGTH, cipherTextWithTag.length);

        // 6. 转为Base64编码返回
        return Base64.getEncoder().encodeToString(result);
    }

    /**
     * 解密：密文（Base64编码）-> 明文
     * @param cipherText Base64编码的密文（格式：IV+密文+认证标签）
     * @param secretKey Base64编码的AES密钥
     * @return 解密后的明文
     */
    public static String decrypt(String cipherText, String secretKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        // 1. 解码密文和密钥
        byte[] resultBytes = Base64.getDecoder().decode(cipherText);
        byte[] keyBytes = Base64.getDecoder().decode(secretKey);
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, ALGORITHM);

        // 2. 拆分IV、密文+标签（前12字节是IV，剩余是密文+标签）
        byte[] iv = new byte[IV_LENGTH];
        System.arraycopy(resultBytes, 0, iv, 0, IV_LENGTH);
        byte[] cipherTextWithTag = new byte[resultBytes.length - IV_LENGTH];
        System.arraycopy(resultBytes, IV_LENGTH, cipherTextWithTag, 0, cipherTextWithTag.length);

        // 3. 初始化解密器（需传入相同的IV和标签长度）
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        GCMParameterSpec gcmSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);

        // 4. 解密（自动验证标签，若数据被篡改会抛出BadPaddingException）
        byte[] plainBytes = cipher.doFinal(cipherTextWithTag);

        // 5. 转为明文字符串
        return new String(plainBytes, StandardCharsets.UTF_8);
    }
}